package cn.mulanbay.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.WebFluxCallbackManager;
import cn.mulanbay.common.config.ServiceNameConstants;
import cn.mulanbay.gateway.handler.SentinelFallbackHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;

/**
 * 网关限流配置
 *
 * @author fenghong
 */
@Configuration
public class GatewayConfig {

    private static final Logger logger = LoggerFactory.getLogger(GatewayConfig.class);

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelFallbackHandler sentinelGatewayExceptionHandler() {
        return new SentinelFallbackHandler();
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 总的针对整个微服务应用基本的key在sentinel dashboard中配置
     */
    @PostConstruct
    public void doInit() {
        initAuthorityRule();
        // 加载网关限流规则(自定义详细细分)，目前在nacos中实现配置
        //initGatewayRules();
    }

    /**
     * 授权规则
     */
    private void initAuthorityRule(){
        /**
         * 访问规则限制
         */
        WebFluxCallbackManager.setRequestOriginParser(new Function<ServerWebExchange, String>() {

            /**
             * 对应配置里面的limitApp
             * 可以选取header中的自定义值，或者其他自定义规则
             *
             * @param serverWebExchange
             * @return
             */
            @Override
            public String apply(ServerWebExchange serverWebExchange) {
                String ip = serverWebExchange.getRequest().getRemoteAddress().getHostString();
                logger.debug("访问来源的IP："+ip);
                return ip;
            }
        });
    }

    /**
     * 网关限流规则(自定义)
     */
    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule(ServiceNameConstants.ADMIN_API_SERVICE)
                .setCount(5) // 限流阈值
                .setIntervalSec(60)); // 统计时间窗口，单位是秒，默认是 1 秒
        rules.add(new GatewayFlowRule(ServiceNameConstants.SERVER_API_SERVICE)
                .setCount(5) // 限流阈值
                .setIntervalSec(60));
        // 加载网关限流规则
        logger.info("加载网关限流规则");
        GatewayRuleManager.loadRules(rules);
        // 加载限流分组
        logger.info("加载限流分组");
        initCustomizedApis();
    }

    /**
     * 限流分组
     */
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        // 后台管理
        ApiDefinition api1 = new ApiDefinition(ServiceNameConstants.ADMIN_API_SERVICE).setPredicateItems(new HashSet<ApiPredicateItem>() {
            private static final long serialVersionUID = 1L;
            {
                // 匹配 /其子路径的所有请求
                add(new ApiPathPredicateItem().setPattern("/admin/**")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }
        });
        // 系统服务
        ApiDefinition api2 = new ApiDefinition(ServiceNameConstants.SERVER_API_SERVICE).setPredicateItems(new HashSet<ApiPredicateItem>() {
            private static final long serialVersionUID = 1L;
            {
                // 临时用户列表
                add(new ApiPathPredicateItem().setPattern("/server/user/tmpList"));
                // 匹配 /其子路径的所有请求
//                add(new ApiPathPredicateItem().setPattern("/server/**")
//                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }
        });
        definitions.add(api1);
        definitions.add(api2);
        // 加载限流分组
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}
